从原理聊JVM:染色标记和垃圾回收算法
Tech导读
JAVA简单易用的特性,能够让研发人员在不了解JVM的底层运行机制的情况下依旧能够编写出功能完善的代码。但是对JVM的理解,是一个程序员普通和优秀的分水岭。全面地了解JVM的工作原理,能够更好地优化自己的代码,并解决一些潜在的性能问题。本文将从原理聊起,把JVM的内存分配、GC、编译等知识进行分析和总结。
导读
JAVA简单易用的特性,能够让研发人员在不了解JVM的底层运行机制的情况下依旧能够编写出功能完善的代码。但是对JVM的理解,是一个程序员普通和优秀的分水岭。全面地了解JVM的工作原理,能够更好地优化自己的代码,并解决一些潜在的性能问题。本文将从原理聊起,把JVM的内存分配、GC、编译等知识进行分析和总结。
01 JVM运行时内存划分
在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!
1.1 运行时数据区域
方法区
堆
虚拟机栈
本地方法栈
程序计数器
1.2 对象的内存布局
在 HotSpot 虚拟机中,对象分为如下3块区域:
对象头(Header)运行时数据:哈希码、GC分代年龄、锁状态标志、偏向线程ID、偏向时间戳等。类型指针:对象的类型元数据的指针,如果对象是数据,还会记录数组长度。
对象实例数据(Instance Data)包含对象真正的内容,即其包括父类所有字段的值。
对齐填充(Padding)对象大小必须是是8字节的整数倍,所以对象大小不满足这个条件时,需要用对齐填充来补齐。
02 标记的方法和流程
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
2.1 判断对象是否需要被回收
引用计数法就是在对象被引用时,计数加1;引用断开时,计数减1。那么一个对象的引用计数为0时,说明这个对象可以被清除。这个算法的问题在于,如果A对象引用B的同时,B对象也引用A,即循环引用,那么虽然双方的引用计数都不为0,但如果仅仅被对方引用实际上没有存在的价值,应该被GC掉。 可达性算法通过引用计数法的缺陷可以看出,从被引用一方去判定其是否应该被清理过于片面,所以可以通过相反的方向去定位对象的存活价值:一个存活对象引用的所有对象都是不应该被清除的(Java中软引用或弱引用在GC时有不同判定表现,不在此深究)。这些查找起点被称为GC Root。
2.2 哪些对象可以作为GC Root呢?
1.JAVA虚拟机栈中的本地变量引用对象
2.方法区中静态变量引用的对象
3.方法区中常量引用的对象
2.3 快速找到GC Root - OopMap
保守式垃圾收集
会直接线性扫描栈,再判断每一串数字是不是引用,而HotSpot采用准确式垃圾收集
方式,所有对象都存放在OopMap(Ordinary Object Pointer)中,当GC发生时,直接从这个map中寻找GC Root。1.类加载完成后,HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来。
2.4 更新OopMap的时机 - 安全点
安全点
。安全点位置的选取的标准是:“是否具有让程序长时间执行”。比如方法调用、循环跳转、异常跳出等等。2.5 可达性分析过程
三色标记法
白色:表示垃圾回收过程中,尚未被垃圾收集器访问过的对象,在可达性分析开始阶段,所有对象都是白色的,即不可达。
黑色:被垃圾收集器访问过的对象,且这个对象所有的引用均扫描过。黑色的对象是安全存活的,如果其他对象被访问时发现其引用了黑色对象,该黑色对象也不会再被扫描。
灰色:被垃圾收集器访问过的对象,但这个对象至少有一个引用的对象没有被扫描过。那么标记阶段就是从GC Root的开始,沿着其引用链将每一个对象从白色标记为灰色最后标记为黑色的过程。
标记过程中不一致问题
由于这个阶段是层层递进的标记,所以过程中难免出现不一致的情况导致原本是黑色的对象被标记为白色,比如,当前扫描到B对象了,C对象尚未被访问时,标记情况如下:
那么如果这时A对象取消了对B对象的引用,而GC Root增加了对C对象的引用,GC Root作为黑色标记不会再次被扫描,那么C对象在标记阶段结束后仍然会保持白色,就会被清除掉。
解决方式
增量更新
原始快照(SATB)
示例
1.GC Root直接引用了C
理论上,C仍然是可达对象,不应被清除,而B不可达,应当被清除。
增量更新会记录行为1,将GC Root标记为灰色,B不能访问到被标记为可以回收:
等到重新标记阶段再次访问灰色的GC Root,顺序将GC Root和C标记为黑色:
03
分代模型
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
3.1 分代假说
跨代引用假说(IntergenerationalReferenceHypothesis):跨代引用相对于同代引用来说仅占极少数。
上述假说是根据实际经验得来的,由此垃圾收集器通常分为“年轻代”和“年老代”:
年轻代用来存放不断生成且生命周期短暂的对象,收集动作相对高频
年老代用来存放经历多次GC仍然存活的对象,收集动作相对低频
3.2 空间分配担保
3.3 记忆集和卡表
记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。
记忆集的作用
卡表
实现记忆集时,可以有不同精度的粒度:可以指向内存地址,也可以指向某个对象,或者指向某一块内存区域。精度越低,维护成本越低。指向某一块内存区域的实现方式就是“卡表”。卡表通常就是一个byte数组,数组中每一个元素代表某一块内存,其值是1或者0:当发生跨代引用时,就表示该元素“dirty”了,那么将将其设置为1,否则就是0。
04 垃圾回收算法
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
4.1 标记-清除(Mark-Sweep)
缺点是清除后会产生不连续的内存碎片。碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得已再次触发GC。
4.2 标记-复制(Mark-Copy)
这种做法,相比普通的两块空间的标记复制算法来说,只有10%的内存空间浪费,而这样做的原因是:大部分情况下,一次young gc后剩余的存活对象非常少。
4.3 标记-整理(Mark-Compact)
此方法避免标记-清除算法的碎片问题,同时也避免了复制算法的空间问题。一般年轻代中执行GC后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以完成收集。
而年老代中因为对象存活率高,用标记复制算法时数据复制效率较低,且空间浪费较大。所以需要使用标记-清除或者标记-整理算法来进行回收。
05 最后
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
大报文问题实战
sharding-jdbc分库连接数优化
软件架构可视化及C4模型,架构设计不仅仅是UML
求分享
求点赞
求在看